package name.fordj.glutil.index;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;

import java.util.*;

public class IndexerBuilder<T> {
    private final EventList<T> sourceList;
    private final ArrayList<T> proxyList = new ArrayList<T>();

    private List<KeyMaker> keyMakerList = new LinkedList<KeyMaker>();
    private Indexer<T> indexer = new Indexer<T>();

    public IndexerBuilder(final EventList<T> sourceList) {
        this.sourceList = sourceList;
    }

    private void initEventListener() {
        try {
            sourceList.getReadWriteLock().readLock().lock();

//            proxyList = new ArrayList<T>(sourceList);
            proxyList.ensureCapacity(sourceList.size());
            proxyList.addAll(sourceList);

            sourceList.addListEventListener(new ListEventListener<T>() {
                public void listChanged(ListEvent<T> listChanges) {

                    while(listChanges.next()) {
                        int index = listChanges.getIndex();
                        int type = listChanges.getType();
                        if(type == ListEvent.INSERT) {
                            T obj = sourceList.get(index);
                            for (KeyMaker keyMaker : keyMakerList) {
                                Object key = keyMaker.makeKey(obj);
                                Index<T> objectEventListMap = indexer.getOrCreateIfNotExist(keyMaker.getId());
                                EventList<T> list = objectEventListMap.getAndCreateIfNotExist(key);
                                try {
                                    list.getReadWriteLock().writeLock().lock();
                                    list.add(obj);
                                } finally {
                                    list.getReadWriteLock().writeLock().unlock();
                                }
                            }
                            proxyList.add(obj);
                        } else if(type == ListEvent.UPDATE) {
                            T proxyObj = proxyList.get(index);
                            T sourceObj = sourceList.get(index);
                            if(proxyObj != sourceObj) {
                                proxyList.set(index, sourceObj);
                            }
                            for (KeyMaker keyMaker : keyMakerList) {
                                Object key = keyMaker.makeKey(sourceObj);
                                Index<T> keyIndex = indexer.get(keyMaker.getId());
                                keyIndex.remove(proxyObj);
                                EventList<T> list = keyIndex.getAndCreateIfNotExist(key);
                                try {
                                    list.getReadWriteLock().writeLock().lock();
                                    list.add(sourceObj);
                                } finally {
                                    list.getReadWriteLock().writeLock().unlock();
                                }
                            }
                        } else {
                            T removed = proxyList.remove(index);
                            for (KeyMaker keyMaker : keyMakerList) {
                                indexer.get(keyMaker.getId()).remove(removed);
                            }
                        }
                    }
                }
            });
        } finally {
            sourceList.getReadWriteLock().readLock().unlock();
        }
    }

    public IndexerBuilder<T> addKeyMaker(KeyMaker keyMaker) {
        keyMakerList.add(keyMaker);
        return this;
    }

    public Indexer<T> build() {
        initEventListener();
        return indexer;
    }
}
